using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;

namespace Athena.Core
{
    /// <summary>
    /// A Node can be a Part or a Species. Nodes have incoming and outgoing Edges in addition
    /// to all the properties of an Item. 
    /// </summary>
    [Serializable()]
    public class Node : Item
    {
        
        protected List<Edge> _IncomingEdges = new List<Edge>(), 
                             _OutgoingEdges = new List<Edge>();

        /// <summary>
        /// Get or Set the ID for this node. Updates all connected Edges
        /// </summary>
        public override string ID
        {
            get { return _ID; }
            set
            {
                if (_ID == value) return;

                string oldID = _ID;

                base.ID = value;

                List<Edge> nodeEdges = AllEdges;
                foreach (Edge e in nodeEdges)
                {
                    e.ChangeName(oldID, ID);
                }                
            }
        }

        /// <summary>
        /// Create the visual version of this Node
        /// </summary>
        /// <param name="point">location where to place the Node</param>
        public override void GenerateDrawable(System.Drawing.PointF point)
        {
            _Drawable = new Objects.SpeciesGlyph(point);
            _Drawable.Item = this;
        }

        /// <summary>
        /// Get all the Edges that have this Node as a product or target
        /// </summary>
        public virtual List<Edge> IncomingEdges
        {
            get
            {
                return _IncomingEdges;
            }
            set
            {
                _IncomingEdges = value;
            }
        }
        /// <summary>
        /// Get all the Edges that have this node as a reactant or modifier
        /// </summary>
        public virtual List<Edge> OutgoingEdges
        {
            get
            {
                return _OutgoingEdges;
            }
            set
            {
                _OutgoingEdges = value;
            }
        }

        /// <summary>
        /// Get all the Edges that have this Node in it
        /// </summary>
        public virtual List<Edge> AllEdges
        {
            get
            {
                List<Edge> edges = new List<Edge>();
                if (_IncomingEdges != null) edges.AddRange(_IncomingEdges);
                if (_OutgoingEdges != null) edges.AddRange(_OutgoingEdges);
                return edges;
            }
        }

        /// <summary>
        /// This bool determines whether this Node is allowed to be a product
        /// </summary>
        public bool InputConnectionsAllowed = false;

        /// <summary>
        /// This bool determines whether this Node is allowed to be a reactant
        /// </summary>
        public bool OutputConnectionsAllowed = false;
        

        /// <summary>
        /// returns a list of all edges attached to this node
        /// </summary>
        public virtual List<Edge> GetEdges()
        {
            List<Edge> edges = new List<Edge>();
            if (_IncomingEdges != null)
                edges.AddRange(_IncomingEdges);
            if (_OutgoingEdges != null)
                edges.AddRange(_OutgoingEdges);
            return edges;

        }

        /// <summary>
        /// creates a duplicate of this node
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new Node(this);
        }

        /// <summary>
        /// Create a node that is a copy of the given node
        /// </summary>
        /// <param name="copy">the original node</param>
        public Node(Node copy)
        {
            _Type = copy._Type;
            _DBID = copy._DBID;
            _ID = copy._ID;
            _Parent = copy._Parent;
            _IncomingEdges = new List<Edge>(copy._IncomingEdges);
            _OutgoingEdges = new List<Edge>(copy._OutgoingEdges);
            InputConnectionsAllowed = copy.InputConnectionsAllowed;
            OutputConnectionsAllowed = copy.OutputConnectionsAllowed;
            //_Parent = copy._Parent;
        }

        /// <summary>
        /// Create a node with default type = "part"
        /// </summary>
        public Node()
            : base("part", "p")
        {
            _IncomingEdges = new List<Edge>();
            _OutgoingEdges = new List<Edge>();
            InputConnectionsAllowed = true;
            OutputConnectionsAllowed = true;
        }

        /// <summary>
        /// Create a node of the given type and with the given ID
        /// </summary>
        /// <param name="type">type of this node</param>
        /// <param name="id">ID of this node</param>
        public Node(string type, string id) : base(type,id) 
        {
            _IncomingEdges = new List<Edge>();
            _OutgoingEdges = new List<Edge>();
            InputConnectionsAllowed = true;
            OutputConnectionsAllowed = true;
        }
        /// <summary>
        /// Is this node valid as an input in a reaction with the given inputs and outputs?
        /// </summary>
        /// <param name="inputs">inputs to the reaction</param>
        /// <param name="outputs">outputs to the reaction</param>
        /// <returns>boolean</returns>
        public override bool ValidReactant(ICollection<Node> inputs, ICollection<Node> outputs)
        {
            if (inputs == null || outputs == null || !OutputConnectionsAllowed)
            {
                LastErrorMessage = ID + " cannot be used as an input";
                return false;
            }
            foreach (Node n in inputs)    //just make sure none of them are null
                if (n == null)
                {
                    LastErrorMessage = "One of the inputs in Null";
                    return false;
                }
            foreach (Node n in outputs)
                if (n == null)
                {
                    LastErrorMessage = "One of the outputs in Null";
                    return false;
                }

            if (_Parent != null)
                if (!(_Parent).ValidReactant(inputs, outputs))
                {
                    LastErrorMessage = _Parent.LastErrorMessage;
                    return false;
                }

            return true;
        }
        /// <summary>
        /// Is this node valid as a product in a reaction with the given inputs and outputs?
        /// </summary>
        /// <param name="inputs">inputs to the reaction</param>
        /// <param name="outputs">outputs to the reaction</param>
        /// <returns>boolean</returns>
        public override bool ValidProduct(ICollection<Node> inputs, ICollection<Node> outputs)
        {
            if (inputs == null || outputs == null || !InputConnectionsAllowed)
            {
                LastErrorMessage = ID + " cannot be used as an output";
                return false;
            }
            foreach (Node n in inputs)    //just make sure none of them are null
                if (n == null)
                {
                    LastErrorMessage = "One of the inputs in Null";
                    return false;
                }
            foreach (Node n in outputs)
                if (n == null)
                {
                    LastErrorMessage = "One of the outputs in Null";
                    return false;
                }

            if (_Parent != null)
                if (!(_Parent).ValidProduct(inputs, outputs))
                {
                    LastErrorMessage = _Parent.LastErrorMessage;
                    return false;
                }

            return true;
        }

        /// <summary>
        /// add an edge where this node is a product
        /// </summary>
        /// <param name="edge">the edge to which this node will be added</param>
        public virtual void AddIncomingEdge(Edge edge)
        {
            if (edge == null) return;
            if (_IncomingEdges == null) _IncomingEdges = new List<Edge>();
            if (!_IncomingEdges.Contains(edge))
                _IncomingEdges.Add(edge);
        }

        /// <summary>
        /// remove an edge that had this node as one of its products
        /// </summary>
        /// <param name="edge">the edge from which to remove this node</param>
        public virtual void RemoveIncomingEdge(Edge edge)
        {
            if (edge == null) return;
            if (_IncomingEdges != null)
                _IncomingEdges.Remove(edge);
        }
        /// <summary>
        /// add an edge with this node as a reactant
        /// </summary>
        /// <param name="edge">the edge to which this node will be added</param>
        public virtual void AddOutgoingEdge(Edge edge)
        {
            if (edge == null) return;
            if (_OutgoingEdges == null) _OutgoingEdges = new List<Edge>();
            if (!_OutgoingEdges.Contains(edge))
                _OutgoingEdges.Add(edge);
        }

        /// <summary>
        /// remove an edge had this node as a reactant
        /// </summary>
        /// <param name="edge">the edge from which to remove this node</param>
        public virtual void RemoveOutgoingEdge(Edge edge)
        {
            if (edge == null) return;
            if (_OutgoingEdges != null)
                _OutgoingEdges.Remove(edge);
        }

        /// <summary>
        /// Remove all the edges going out from this node
        /// </summary>
        public virtual void RemoveAllOutgoingEdges()
        {
            List<Edge> edges = new List<Edge>();
            edges.AddRange(_OutgoingEdges);

            foreach (Edge e in edges)
            {
                e.RemoveReactant(this);                
                _OutgoingEdges.Remove(e);
            }
        }

        /// <summary>
        /// Remove all the edges coming into this node
        /// </summary>
        public virtual void RemoveAllIncomingEdges()
        {
            List<Edge> edges = new List<Edge>();
            edges.AddRange(_IncomingEdges);

            foreach (Edge e in edges)
            {
                e.RemoveProduct(this);
                _IncomingEdges.Remove(e);
            }
        }

        /// <summary>
        /// Remove all the edges from this species
        /// </summary>
        public override void Remove()
        {
            base.Remove();
            
            if (_IncomingEdges != null)
            {
                RemoveAllIncomingEdges();
            }

            if (_OutgoingEdges != null)
            {
                RemoveAllOutgoingEdges();
            }
        }

        /// <summary>
        /// Writes the XML for this class
        /// </summary>
        /// <param name="writer">xml writer</param>
        public override void WriteXML(XmlWriter writer)
        {
            base.WriteXML(writer);
        }

        /// <summary>
        /// Reads the information from an XML file
        /// </summary>
        /// <param name="reader">xlm reader</param>
        public override bool ReadXML(XmlReader reader)
        {
            bool value = base.ReadXML(reader);
            return value;
        }
    }
}
